/*
    Copyright (C) 2010-2011 mdis
    
    This file is part of mdis.
    Written by Ketfi-Chérif Mehdi <ketficherif.mehdi@gmail.com>

    mdis is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    mdis is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with mdis.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "global.h"

tui_colors_t tui_colors[] = 
{
	{ TUI_CDIALOG_BKGD , COLOR_WHITE , COLOR_WHITE },
	{ TUI_CDIALOG_BFRAME , COLOR_WHITE , COLOR_WHITE },
	{ TUI_CDIALOG_FRAME , COLOR_BLACK , COLOR_WHITE },
	{ TUI_CDIALOG_TEXT , COLOR_BLACK , COLOR_WHITE } ,
	{ TUI_TEXT_BLACK_WHITE , COLOR_BLACK , COLOR_WHITE } ,
	{ TUI_TEXT_WHITE_BLACK , COLOR_WHITE , COLOR_BLACK } ,
	{ TUI_TEXT_WHITE_BLUE , COLOR_WHITE , COLOR_BLUE } ,
	{ TUI_TEXT_RED_BLACK , COLOR_RED , COLOR_BLACK } ,
	{ TUI_SHADOW , COLOR_BLACK , COLOR_BLACK }
};

void
 tui_init ( void )
{
	initscr ();
	noecho ();
	cbreak ();
	curs_set ( 0 );
	keypad ( stdscr , TRUE );
}

void
 tui_init_colors ( void )
{
	uint16_t i;

	start_color ();

	for ( i = 0; i < sizeof (tui_colors)/sizeof(tui_colors[0]) ; i++ )
		init_pair ( tui_colors[i].pair , tui_colors[i].fore , tui_colors[i].bkgd );
}

pstatic_text_t
 tui_static_text_create ( uint16_t width , uint16_t height ,
		 	  uint16_t xp , uint16_t yp , int attrib ,
		 	  int16_t bkgd_color , uint8_t format ,
			  char * str )
{
	pstatic_text_t st = NULL;
	st = calloc ( 1 , sizeof (static_text_t) );
	if ( st == NULL )
		return NULL;
	st->text = calloc ( strlen (str)+1 , sizeof (char) );
	st->w = width;
	st->h = height;
	st->x = xp;
	st->y = yp;
	sprintf ( st->text , "%s" , str );
	st->win = newwin ( st->h , st->w , st->y , st->x );
	wattrset ( st->win , attrib );
	wbkgd ( st->win , bkgd_color );
	switch ( format )
	{
		case TUI_TEXT_LEFT:
			wprintw ( st->win , "%s" , st->text );
			break;
		case TUI_TEXT_RIGHT:
			if ( st->w > strlen (st->text) )
				mvwprintw ( st->win , 0 , st->w-strlen(st->text) , "%s" , str );
			break;
		case TUI_TEXT_CENTER:
			if ( st->w > strlen (st->text) )
				mvwprintw ( st->win ,  0 , (st->w-strlen(st->text))/2 , "%s" , str );
			break;
	}
	return st;
}

void
 tui_static_text_show ( pstatic_text_t st )
{
	wrefresh ( st->win );
}

void
 tui_static_text_destroy ( pstatic_text_t st )
{
	if ( st != NULL )
	{
		delwin ( st->win );
		free ( st->text );
		free ( st );
	}
}

pmwindow_t
 tui_mwindow_create ( uint16_t width , uint16_t height , uint16_t xpos ,
		      uint16_t ypos , char *title )
{
	pmwindow_t mwin = calloc ( 1 , sizeof ( mwindow_t ) );
	if ( !mwin )
		return NULL;
	mwin->x = xpos;
	mwin->y = ypos;
	mwin->w = width;
	mwin->h = height;
	mwin->win = newwin ( mwin->h , mwin->w , mwin->y , mwin->x );
	box ( mwin->win , ACS_VLINE , ACS_HLINE );
	mvwprintw ( mwin->win , 0 , 4 , " %s " , title );
	mvwaddch ( mwin->win , 0 , 3 , ACS_RTEE );
	mvwaddch ( mwin->win , 0 , strlen(title)+6 , ACS_LTEE );
	return mwin;
}

void
 tui_mwindow_set_title ( pmwindow_t mwin , char *title )
{
	box ( mwin->win , ACS_VLINE , ACS_HLINE );
	mvwprintw ( mwin->win , 0 , 4 , " %s " , title );
	mvwaddch ( mwin->win , 0 , 3 , ACS_RTEE );
	mvwaddch ( mwin->win , 0 , strlen(title)+6 , ACS_LTEE );
}

void
 tui_mwindow_show ( pmwindow_t mwin )
{
	touchwin ( mwin->win );
	wrefresh ( mwin->win );
}

void
 tui_mwindow_destroy ( pmwindow_t mwin )
{
	if ( mwin != NULL )
	{
		delwin ( mwin->win );
		free ( mwin );
	}
}

ptui_listview_t
 tui_listview_create ( WINDOW * parent , uint16_t width , uint16_t height ,
			uint16_t xpos , uint16_t ypos )
{
	ptui_listview_t listview = NULL;
	uint16_t i;
	listview = calloc ( 1 , sizeof (tui_listview_t) );
	listview->w = width;
	listview->h = height;
	listview->x = xpos;
	listview->y = ypos;
	listview->maxitems = height;
	listview->nitems = 0;
	listview->citem = 0;
	listview->fitem = 0;
	listview->win = parent;
	listview->witems = calloc ( listview->maxitems , sizeof ( WINDOW * ) );
	for ( i = 0; i < listview->maxitems ; i++ )
		listview->witems[i] = derwin ( listview->win , 1 , listview->w , i+1 , 1 );
	return listview;
}

void
 tui_listview_add_item ( ptui_listview_t listview , char * item )
{
	listview->items = realloc ( listview->items , (listview->nitems+1)*sizeof(char*) );
	listview->items[listview->nitems] = calloc ( strlen(item)+1 , sizeof (char) );
	sprintf ( listview->items[listview->nitems] , "%s" , item );
	listview->nitems++;
}

void
 tui_listview_show ( ptui_listview_t listview )
{
	uint16_t i;
	for ( i = 0; i < ((listview->nitems>listview->maxitems) ? listview->maxitems : listview->nitems) ; i++ )
	{
		if ( i == listview->citem )
			if ( listview->focus )
				wbkgd ( listview->witems[i] , C(TUI_TEXT_WHITE_BLUE)  );
			else
				wbkgd ( listview->witems[i] , C(TUI_TEXT_BLACK_WHITE) );
		else
			wbkgd ( listview->witems[i] , C(TUI_TEXT_WHITE_BLACK) );
		werase ( listview->witems[i] );
		mvwprintw ( listview->witems[i] , 0 , 0 , "%s" , listview->items[listview->fitem+i]  );
		wrefresh ( listview->witems[i] );
	}
	wrefresh ( listview->win );
}

void
 tui_listview_set_focus ( ptui_listview_t listview , uint8_t focus )
{
	listview->focus = focus;
	tui_listview_show ( listview );
}

void
 tui_listview_select_next ( ptui_listview_t listview )
{
	if ( listview->citem+1 < ((listview->nitems>listview->maxitems) ? listview->maxitems:listview->nitems) )
		listview->citem++;
	else
		if ( listview->nitems > listview->maxitems )
			if ( listview->fitem < (listview->nitems-listview->maxitems) )
				listview->fitem++;
}

void
 tui_listview_select_prev ( ptui_listview_t listview )
{
	if ( listview->citem != 0 )
		listview->citem--;
	else
		if ( listview->fitem != 0 )
			listview->fitem--;
}

void
 tui_listview_select_snext ( ptui_listview_t listview )
{
	uint16_t n = ((listview->nitems>listview->maxitems) ? listview->maxitems:listview->nitems)/2;
	if ( listview->citem+n < ((listview->nitems>listview->maxitems) ? listview->maxitems-1:listview->nitems-1) )
		listview->citem += n;
	else
	{
		if ( (listview->nitems > listview->maxitems) && (listview->citem == (listview->nitems>listview->maxitems) ? listview->maxitems-1:listview->nitems-1) )
		{
			if ( listview->fitem+n < (listview->nitems-listview->maxitems) )
				listview->fitem += n;
			else
				listview->fitem = listview->nitems-listview->maxitems;
		}
		if ( listview->citem != (listview->nitems>listview->maxitems) ? listview->maxitems-1:listview->nitems-1 )
			listview->citem = (listview->nitems>listview->maxitems) ? listview->maxitems-1:listview->nitems-1;
	}
}

void
 tui_listview_select_sprev ( ptui_listview_t listview )
{
	uint16_t n = listview->maxitems/2;
	if ( (int)(listview->citem-n) > 0 )
		listview->citem -= n;
	else
	{
		if ( (int)(listview->fitem-n) > 0  )
		{
			if ( listview->citem == 0 )
				listview->fitem -= n;
		}
		else
			listview->fitem = 0;
		if ( listview->citem != 0 )
			listview->citem = 0;
	}
}

void
 tui_listview_destroy ( ptui_listview_t listview )
{
	uint16_t i;
	if ( listview != NULL )
	{
		if ( listview->items != NULL )
		{
			for ( i = 0; i < listview->nitems ; i++ )
			{
				if ( listview->items[i] != NULL )
					free ( listview->items[i] );
			}
			free ( listview->items );
		}
		if ( listview->witems != NULL )
		{
			for ( i = 0; i < listview->maxitems ; i++ )
				delwin ( listview->witems[i] );
			free ( listview->witems );
		}
		free ( listview );
	}
}


ptui_hexview_t
 tui_hexview_create ( WINDOW * parent , uint16_t width , uint16_t height , uint16_t xpos ,
		 	uint16_t ypos )
{
	ptui_hexview_t hv = NULL;
	hv = calloc ( 1 , sizeof ( tui_hexview_t ) );
	if ( hv == NULL )
		return NULL;
	hv->w = width;
	hv->h = height;
	hv->x = xpos;
	hv->y = ypos;
	hv->win = derwin ( parent , hv->h , hv->w , hv->y , hv->x );
	return hv;
}

void
 tui_hexview_set ( ptui_hexview_t hv , uint8_t * buffer , uint16_t size )
{
	hv->data = buffer;
	hv->nbytes = size;
	hv->start = hv->curs = 0;
}

void
 tui_hexview_show ( ptui_hexview_t hv )
{
	uint16_t addr = hv->start , y = 0 , i = 0 , n = 0;
	while ( (addr < (hv->nbytes-1)) && (y < hv->h) )
	{
		if ( addr+16 < hv->nbytes )
			n = 16;
		else
			n = hv->nbytes-addr;
		wattr_on ( hv->win , C(TUI_TEXT_WHITE_BLACK) , NULL );
		mvwprintw ( hv->win , y , 0 , "%04X" , addr );
		for ( i = 0; i < n; i++ )
		{
			wattr_on ( hv->win , C(TUI_TEXT_WHITE_BLACK) , NULL );
			if ( addr+i == hv->curs )
			{
				if ( hv->focus )
					wattr_on ( hv->win , C(TUI_TEXT_WHITE_BLUE) , NULL );
				else
					wattr_on ( hv->win , C(TUI_TEXT_BLACK_WHITE) , NULL );
			}
			mvwprintw ( hv->win , y , i*3+6 , "%02X" , hv->data[addr+i] );
		}
		for ( i = 0; i < n; i++ )
		{
			wattr_on ( hv->win , C(TUI_TEXT_WHITE_BLACK) , NULL );
			if ( addr+i == hv->curs )
			{
				if ( hv->focus )
					wattr_on ( hv->win , C(TUI_TEXT_WHITE_BLUE) , NULL );
				else
					wattr_on ( hv->win , C(TUI_TEXT_BLACK_WHITE) , NULL );
			}
			if ( (hv->data[addr+i] > 'a' && hv->data[addr+i] < 'z') ||
			   	(hv->data[addr+i] > 'A' && hv->data[addr+i] < 'Z' ))
				mvwprintw ( hv->win , y , i+55 , "%c" , hv->data[addr+i] );
			else
				mvwprintw ( hv->win , y , i+55 , "." );
		}
		addr = addr + ((addr+16>hv->nbytes)?hv->nbytes-addr:16);
		y++;
	}
	wrefresh ( hv->win );
}

void
 tui_hexview_right ( ptui_hexview_t hv )
{
	if ( hv->curs < (hv->start+hv->h*16-1) )
		hv->curs++;
}

void
 tui_hexview_left ( ptui_hexview_t hv )
{
	if ( hv->curs > hv->start )
		hv->curs--;
}

void
 tui_hexview_down ( ptui_hexview_t hv )
{
	if ( hv->curs+16<=(hv->start+hv->h*16-1) )
		hv->curs = hv->curs + 16;
	else
	{
		if ( hv->curs+16 < hv->nbytes-1 )
		{
			hv->start = hv->start + 16;
			hv->curs = hv->curs + 16;
		}
	}
}

void
 tui_hexview_up ( ptui_hexview_t hv )
{
	if ( hv->curs-16 >= hv->start )
		hv->curs = hv->curs - 16;
	else
	{
		if ( (int16_t)(hv->start-16) >= 0 )
		{
			hv->start = hv->start - 16;
			hv->curs = hv->curs - 16;
		}
		else
			hv->start = 0;

	}
}

void
 tui_hexview_set_focus ( ptui_hexview_t hv , uint8_t focus )
{
	hv->focus = focus;
}

void
 tui_hexview_destroy ( ptui_hexview_t hv )
{
	if ( hv != NULL )
	{
		delwin ( hv->win );
		free ( hv );
	}
}

void
 tui_wframe ( WINDOW *window , uint16_t width , uint16_t height )
{
	uint16_t i;
	for ( i = 0; i < width ; i++ )
	{
		wattr_on ( window , A_BOLD | C(TUI_CDIALOG_BFRAME) , NULL );
		mvwaddch ( window , 0 , i+1 , ACS_HLINE );
		wattr_off ( window , A_BOLD | C(TUI_CDIALOG_BFRAME) , NULL );
		wattr_on ( window , A_BOLD | C(TUI_CDIALOG_FRAME) , NULL );
		mvwaddch ( window , height-1 , i+1 , ACS_HLINE );
		wattr_off ( window , A_BOLD | C(TUI_CDIALOG_FRAME) , NULL );
	}
	for ( i = 0; i < height-1 ; i++ )
	{
		wattr_on ( window , A_BOLD | C(TUI_CDIALOG_BFRAME) , NULL );
		mvwaddch ( window , i+1 , 0 , ACS_VLINE );
		wattr_off ( window , A_BOLD | C(TUI_CDIALOG_BFRAME) , NULL );
		wattr_on ( window , A_BOLD | C(TUI_CDIALOG_FRAME) , NULL );
		mvwaddch ( window , i+1 , width-1 , ACS_VLINE );
		wattr_off ( window , A_BOLD | C(TUI_CDIALOG_FRAME) , NULL );
	}
	wattr_on ( window , A_BOLD | C(TUI_CDIALOG_BFRAME) , NULL );
	mvwaddch ( window , 0 , 0 , ACS_ULCORNER );
	mvwaddch ( window , height-1 , 0 , ACS_LLCORNER );
	wattr_off ( window , A_BOLD | C(TUI_CDIALOG_BFRAME) , NULL );
	wattr_on ( window , A_BOLD | C(TUI_CDIALOG_FRAME) , NULL );
	mvwaddch ( window , 0 , width-1 , ACS_URCORNER );
	mvwaddch ( window , height-1 , width-1 , ACS_LRCORNER );
	wattr_off ( window , A_BOLD | C(TUI_CDIALOG_FRAME) , NULL );
}

void
 tui_whsplit ( WINDOW *window , uint16_t width , uint16_t y )
{
	uint16_t i;
	for ( i = 1 ; i < width ; i++ )
	{
		wattr_on ( window , A_BOLD | C(TUI_CDIALOG_BFRAME) , NULL );
		mvwaddch ( window , y , i , ACS_HLINE );
		wattr_off ( window , A_BOLD | C(TUI_CDIALOG_BFRAME) , NULL );
	}
	wattr_on ( window , A_BOLD | C(TUI_CDIALOG_BFRAME) , NULL );
	mvwaddch ( window , y , 0 , ACS_LTEE );
	wattr_off ( window , A_BOLD | C(TUI_CDIALOG_BFRAME) , NULL );
	wattr_on ( window , A_BOLD | C(TUI_CDIALOG_FRAME) , NULL );
	mvwaddch ( window , y , width-1 , ACS_RTEE );
	wattr_off ( window , A_BOLD | C(TUI_CDIALOG_FRAME) , NULL );
}

void
 tui_msgbox ( char *msg )
{
	uint16_t nlines = strnc ( msg , '\n' ) + 7;
	uint16_t ncols;
	if ( nlines == 7 )
		ncols = strlen ( msg ) + 8;
	else
		ncols = strlenln ( msg ) + 8;
	WINDOW * msgbox = newwin ( nlines , ncols , (scrmaxy-nlines)/2 , (scrmaxx-ncols)/2 );
	wbkgd ( msgbox , C(TUI_CDIALOG_BKGD) );
	wattr_on ( msgbox , A_NORMAL | C(TUI_TEXT_BLACK_WHITE) , NULL );
	mvwprintw ( msgbox , 2 , 4 , "%s" , msg );
	wattr_on ( msgbox , A_BOLD | C(TUI_TEXT_WHITE_BLUE) , NULL );
	mvwprintw ( msgbox , nlines-2 , (ncols-6)/2 , "< OK >" );
	wattr_off ( msgbox , A_BOLD | C(TUI_TEXT_WHITE_BLUE) , NULL );
	tui_wframe ( msgbox , ncols , nlines );
	tui_whsplit ( msgbox , ncols , nlines-3 );
	refresh ();
	touchwin ( msgbox );
	wrefresh ( msgbox );
	getch ();
	delwin ( msgbox );
	touchwin ( stdscr );
	refresh ();
}

void
 tui_close ( void )
{
	endwin ();
}

